using FluentParser.Expressions.Rules.Composition;
using FluentParser.Expressions.Terminals.Parsing;
using FluentParser.Expressions.Rules.Decoration;

namespace FluentParser.Expressions
{
    /// <summary>
    /// A symbol is an expression which returns no value. 
    /// There are only two possible results: success, failure.
    /// Note: A symbol DOES return a value of type object but 
    ///       but it is hidden and never used.
    /// </summary>
    public abstract class Symbol : BaseExpr<object>
    {
        /// <summary>
        /// Transforms a string into a Symbol when a Symbol is expected.
        /// </summary>
        /// <param name="input">Text parsed by the new symbol</param>
        /// <returns>The new symbol</returns>
        /// <example>
        /// Symbol parseText = "Text";
        /// </example>
        public static implicit operator Symbol(string input)
        {
            return new ParseExactSymbolTerminal(input);
        }

        /// <summary>
        /// Combine two symbols using the choice rule. This allows to fallback to
        /// the second symbol in the cast first one fails.
        /// </summary>
        /// <param name="left">The first symbol</param>
        /// <param name="right">The second symbol</param>
        /// <returns>The combined symbol</returns>
        public static ChoiceSymbolRule operator |(Symbol left, Symbol right)
        {
            return new ChoiceSymbolRule(left, right);
        }

        /// <summary>
        /// Combine two symbols using a greedy choice rule. This type of combination rule
        /// is mostly useful for error handling.
        /// </summary>
        /// <param name="left">The first symbol</param>
        /// <param name="right">The second symbol</param>
        /// <returns>The combined symbol</returns>
        public static Symbol operator &(Symbol left, Symbol right)
        {
            return new GreedyChoiceSymbolRule(left, right);
        }

        /// <summary>
        /// Combines two symbols using the grouping operator. This rule forces
        /// both symbols to succeed in the correct order where second symbol starts
        /// parsing where first one ended.
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        public static Symbol operator +(Symbol left, Symbol right)
        {
            return new GroupSymbolRule(left, right);
        }
    }
}